package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Component
@Aspect
public class LoginAspect {
    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 登录验证
     * @param point
     * @param login
     * @return
     */
    //定义切点
    @Around("@annotation(login)")
    public Object loginVerification(ProceedingJoinPoint point, Login login) {
        /**
         * 校验用户是否登录，先从请求中获取请求对象，如果请求对象的请求投中有token，则说明用户登录过
         * 然后去redis中找有没有这个token判断是否登录
         * 请求头或redis中没有token说明用户没有登录，抛出特定的异常
         */
        //获取请求信息
        ServletRequestAttributes servletRequestAttributes
                = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取请求体
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //获取请求头
        String token = request.getHeader("token");
        //拼接redis的key
        String redisKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
        //判断注解中的required是否为true，如果是说明加了注解就要进行登录校验
        if (login.required()) {
            //为true就判断是不是有token
            if (StringUtils.isEmpty(token)) {
                //为空就是没有登录，抛出自定一的异常
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
            //不为空证明登录过，那就看看redis里面有没有，如果没有说明只是登录过，但是现在没有登录
            UserInfo redisValue = (UserInfo) redisTemplate.opsForValue().get(redisKey);
            //看看是不是为空，为空说明redis里没有当前token
            if (null == redisValue) {
                //为空就是没有登录，抛出自定一的异常
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            }
        }

        /**
         * 现在要解决一下，怎么从线程中获取userid供后续使用的，这个操作不用验证是否登录，只需要有token即可
         */
        if (!StringUtils.isEmpty(token)) {
            //有token就尝试去redis中拿用户的信息
            UserInfo redisValue = (UserInfo) redisTemplate.opsForValue().get(redisKey);
            if (null != redisValue) {
                //不为空就放到线程变量中
                /**
                 * AuthContextHolder设置的两个变量
                 * private static ThreadLocal<Long> userId = new ThreadLocal<Long>();
                 * private static ThreadLocal<String> username = new ThreadLocal<String>();
                 */
                AuthContextHolder.setUserId(redisValue.getId());
            }
        }

        //继续执行切点之后的代码
        Object proceed;
        try {
            //原方法的返回值:调用原方法并返回值
            proceed = point.proceed();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        } finally {
            //释放线程资源
            AuthContextHolder.removeUserId();
        }

        return proceed;
    }
}
